/**
 * 
 */
package jmine.tec.utils.collection;

import java.io.Serializable;
import java.util.AbstractCollection;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;

/**
 * {@link Collection} that filter out its entries based on a given predicate. This collection is lazily evaluated, based on the current
 * state of the given collection. The {@link FilteredIterator} inner class can be used separately.
 * <p>
 * This collection is generally thread safe, provided that the wrapper collection is also thread safe.
 * 
 * @author takeshi
 * @param <E> the filtered type
 */
public class FilteredCollection<E> extends AbstractCollection<E> implements Collection<E>, Serializable {

    /**
     * {@link Iterator} implementation that filters out entries that do not match the given predicate. This is only a view of a collection
     * and, as such, its method {@link #remove()} is unsupported.
     * <p/>
     * This iterator is not thread safe and, therefore, should not be shared and does not provide any happens-before boundary
     * 
     * @author takeshi
     */
    public static final class FilteredIterator<E> implements Iterator<E>, Serializable {
        private final Iterator<? extends E> complete;

        private final Predicate<? super E> predicate;

        private E current;

        /**
         * Constructor
         * 
         * @param complete the {@link Iterable} that contains the complete collection to be filtered
         * @param predicate the predicate to match against
         */
        public FilteredIterator(Iterable<? extends E> complete, Predicate<? super E> predicate) {
            super();
            this.complete = complete.iterator();
            this.predicate = predicate;
            this.nextEntry();
        }

        /**
         * updates
         */
        private void nextEntry() {
            this.current = null;
            while (this.complete.hasNext()) {
                E next = this.complete.next();
                if (this.predicate.matches(next)) {
                    this.current = next;
                    break;
                }
            }
        }

        /**
         * @return boolean
         * @see java.util.Iterator#hasNext()
         */
        public boolean hasNext() {
            return this.current != null;
        }

        /**
         * @return the next
         * @see java.util.Iterator#next()
         */
        public E next() {
            if (this.current == null) {
                throw new NoSuchElementException();
            }
            E old = this.current;
            this.nextEntry();
            return old;
        }

        /**
         * unsupported
         * 
         * @see java.util.Iterator#remove()
         */
        public void remove() {
            throw new UnsupportedOperationException("cannot remove elements from a view!");
        }
    }

    private final Predicate<? super E> predicate;

    private final Collection<? extends E> original;

    /**
     * @param original the original filtered
     * @param pred the predicate
     */
    public FilteredCollection(final Collection<? extends E> original, final Predicate<? super E> pred) {
        super();
        this.original = original;
        this.predicate = pred;
    }

    /**
     * @return the iterator
     * @see java.util.Collection#iterator()
     */
    @Override
    public Iterator<E> iterator() {
        return new FilteredIterator<E>(this.original, this.predicate);
    }

    /**
     * @return the size
     * @see java.util.Collection#size()
     */
    @Override
    public int size() {
        int i = 0;
        for (Iterator<E> it = this.iterator(); it.hasNext();) {
            it.next();
            i++;
        }
        return i;
    }

}
